package driver

import (
	"sync"
	"sync/atomic"
	"time"

	"gitee.com/gousing/cache/internal"
	"gitee.com/gousing/cache/storage"
)

// L2Cache Local+Remote 二级缓存实例结构体 (通常为 MemoryCache + RedisCache)
type L2Cache struct {
	local       storage.Storager // Local 实例, 通常为 MemoryCache
	remote      storage.Storager // Remote 实例, 通常为 RedisCache
	localState  bool             // Local 是否工作状态
	remoteState bool             // Remote 是否工作状态
	mu          sync.Mutex       // 互斥锁, 用于保护并发访问时的数据一致性
	locks       []sync.Mutex     // 单实例锁, 限制单个实例(主线程)应用内的并发执行, 初始化256个Key hash分片锁
	isClosed    atomic.Bool      // 缓存实例是否已经关闭
	isGlobal    bool             // 是否是全局缓存实例，如果是则不进行过期时间处理
}

// L2CacheOptions 缓存配置选项，用于初始化 L2 缓存实例时设置相关参数
//
//	预留配置选项，后续扩展使用
type L2CacheOptions struct{}

var _ storage.Storager = (*L2Cache)(nil)

// NewL2Cache 创建并初始化一个 L2Cache 二级缓存实例
//   - 需要传入 local 和 remote 缓存驱动实例  (通常为 MemoryCache + RedisCache)
func NewL2Cache(local storage.Storager, remote storage.Storager, options *L2CacheOptions) (*L2Cache, error) {
	if local == nil || remote == nil {
		return nil, storage.ErrStorageDriverIsNil
	}
	l2 := &L2Cache{
		local:    local,
		remote:   remote,
		isGlobal: false,
	}
	// 初始化单个实例锁分片
	l2.locks = make([]sync.Mutex, 256)
	for i := range 256 {
		l2.locks[i] = sync.Mutex{}
	}
	return l2, nil
}

// NewL2CacheDefualt 创建一个新的 L2Cache 二级缓存实例, 使用默认配置信息
//   - local 默认使用 NewMemoryDefault 创建
//   - remote 默认使用 NewRedisDefault 创建
func NewL2CacheDefualt() (*L2Cache, error) {
	local, err := NewMemoryDefault()
	if err != nil {
		return nil, err
	}
	remote, err := NewRedisDefault()
	if err != nil {
		return nil, err
	}
	return NewL2Cache(local, remote, nil)
}

func (c *L2Cache) getSelfLock(key string) *sync.Mutex {
	// 256-1 = 255, 取余数做为锁的分片索引
	return &c.locks[internal.HashFnv32(key)&255]
}

// loadFromRedis 从 Redis 加载数据到内存缓存中
func (c *L2Cache) loadFromRedis(key string) error {
	if key == "" {
		return storage.ErrKeyIsEmpty
	}
	val, err := c.remote.GetEntry(key)
	if err != nil {
		return err
	}
	return c.local.Set(key, val.Value, storage.WithExpire(val.Expire), storage.WithTags(val.Tags...))
}

// Scan 获取键对应的值, 并解码到 refVal
//   - refVal 是一个指向具体数据类型的指针, 用于存放反序列化后的数据
//   - 不存在或已经过期或反序列化失败则返回错误
//   - 标量类型直接解析到 refVal
//   - 其他返回数据为 msgpack 自动反序列化后解析到 refVal
func (c *L2Cache) Scan(key string, refVal any) error {
	if key == "" {
		return storage.ErrKeyIsEmpty
	}
	if refVal == nil {
		return storage.ErrRefValueIsNil
	}
	sLock := c.getSelfLock(key)
	sLock.Lock()
	defer sLock.Unlock()

	err := c.local.Scan(key, refVal)
	if err == nil {
		return nil
	}
	err = c.remote.Scan(key, refVal)
	if err == nil {
		c.loadFromRedis(key)
	}
	return err
}

// Get 获取键对应的值
//   - 不存在或如果已经过期则返回错误
//   - 存在则返回缓存值:
//   - - 标量类型直接返回 Any 缓存数据值
//   - - 其他返回数据为 msgpack 自动反序列化后的 Any 缓存数据值
func (c *L2Cache) Get(key string) (any, error) {
	if key == "" {
		return nil, storage.ErrKeyIsEmpty
	}
	sLock := c.getSelfLock(key)
	sLock.Lock()
	defer sLock.Unlock()

	val, err := c.local.Get(key)
	if err == nil {
		return val, nil
	}
	val, err = c.remote.Get(key)
	if err == nil {
		c.loadFromRedis(key)
	}
	return val, err
}

// GetEntry 获取键对应的缓存项副本信息, 如果不存在或已经过期则返回错误
func (c *L2Cache) GetEntry(key string) (entry *storage.Entry, err error) {
	if key == "" {
		return nil, storage.ErrKeyIsEmpty
	}
	sLock := c.getSelfLock(key)
	sLock.Lock()
	defer sLock.Unlock()

	entry, err = c.local.GetEntry(key)
	if err == nil {
		return entry, nil
	}
	entry, err = c.remote.GetEntry(key)
	if err == nil {
		c.loadFromRedis(key)
	}
	return entry, err
}

// GetOrSet 获取键对应的值, 如不存在则设置为指定的值
//   - 不存在或如果已经过期则进行设置并返回设置的值
//   - 存在则返回旧值:
//   - - 标量类型直接返回 Any 缓存数据值
//   - - 其他返回数据为 msgpack 自动反序列化后的 Any 缓存数据值
func (c *L2Cache) GetOrSet(key string, value any, opts ...storage.EntryOptions) (any, error) {
	if key == "" {
		return nil, storage.ErrKeyIsEmpty
	}

	sLock := c.getSelfLock(key)
	sLock.Lock()
	defer sLock.Unlock()

	val, err := c.local.Get(key)
	if err == nil {
		return val, nil
	}
	val, err = c.remote.GetOrSet(key, value, opts...)
	if err == nil {
		c.loadFromRedis(key)
	}
	return val, err
}

// GetAndSet 获取键对应的旧值并设置新值
//   - 不存在或如果已经过期则返回nil
//   - 存在则返回旧值:
//   - - 标量类型直接返回 Any 缓存数据值
//   - - 其他返回数据为 msgpack 自动反序列化后的 Any 缓存数据值
func (c *L2Cache) GetAndSet(key string, value any, opts ...storage.EntryOptions) (oldVal any, err error) {
	if key == "" {
		return nil, storage.ErrKeyIsEmpty
	}
	if value == nil {
		return nil, storage.ErrValueIsNil
	}
	sLock := c.getSelfLock(key)
	sLock.Lock()
	defer sLock.Unlock()

	// 先从Redis中获取并设置
	oldVal, err = c.remote.GetAndSet(key, value, opts...)
	if err == nil {
		c.local.Set(key, value, opts...)
		return
	}
	// 再从内存中获取并设置
	oldVal, err = c.local.GetAndSet(key, value, opts...)
	return
}

// GetAndDelete 获取键对应的值, 并删除该键
//   - 不存在或如果已经过期则返回错误
//   - 存在则返回值:
//   - - 标量类型直接返回 Any 缓存数据值
//   - - 其他返回数据为 msgpack 自动反序列化后的 Any 缓存数据值
func (c *L2Cache) GetAndDelete(key string) (any, error) {
	if key == "" {
		return nil, storage.ErrKeyIsEmpty
	}
	sLock := c.getSelfLock(key)
	sLock.Lock()
	defer sLock.Unlock()

	// 先从Redis中获取并删除
	val, err := c.remote.GetAndDelete(key)
	if err == nil {
		c.local.Delete(key)
		return val, nil
	}
	// 再从内存中获取并删除
	return c.local.GetAndDelete(key)
}

// GetTags 获取所有Tag标签
func (c *L2Cache) GetTags() []string {
	return c.remote.GetTags()
}

// GetTagKeys 获取指定 Tag 标签对应的所有缓存 Keys 键名
func (c *L2Cache) GetTagKeys(tag string) []string {
	if tag == "" {
		return []string{}
	}
	return c.remote.GetTagKeys(tag)
}

// GetKeyTags 获取指定键名 Key 对应的所有缓存 Tags 标签
func (c *L2Cache) GetKeyTags(key string) []string {
	if key == "" {
		return []string{}
	}
	return c.remote.GetKeyTags(key)
}

// Set 设置键值对，如果空间不够则按 LRU 机制删除最少使用的数据
func (c *L2Cache) Set(key string, value any, opts ...storage.EntryOptions) error {
	if key == "" {
		return storage.ErrKeyIsEmpty
	}
	if value == nil {
		return storage.ErrValueIsNil
	}
	sLock := c.getSelfLock(key)
	sLock.Lock()
	defer sLock.Unlock()

	c.remote.Set(key, value, opts...)
	return c.local.Set(key, value, opts...)
}

// SetWithExists 仅当缓存 Key 键存在时才更新设置值, 返回True表示更新成功
func (c *L2Cache) SetWithExists(key string, value any, opts ...storage.EntryOptions) (updated bool, err error) {
	if key == "" {
		return false, storage.ErrKeyIsEmpty
	}
	sLock := c.getSelfLock(key)
	sLock.Lock()
	defer sLock.Unlock()

	// 先Redis中处理
	updated, err = c.remote.SetWithExists(key, value, opts...)
	if err == nil {
		if updated {
			err = c.local.Set(key, value, opts...)
			return err == nil, err
		}
		return
	}
	return c.local.SetWithExists(key, value, opts...)
}

// SetWithNotExists 仅当缓存 Key 键不存在时才设置值, 返回True表示设置成功
func (c *L2Cache) SetWithNotExists(key string, value any, opts ...storage.EntryOptions) (added bool, err error) {
	if key == "" {
		return false, storage.ErrKeyIsEmpty
	}
	sLock := c.getSelfLock(key)
	sLock.Lock()
	defer sLock.Unlock()

	// 现在Redis中处理
	added, err = c.remote.SetWithNotExists(key, value, opts...)
	if err == nil {
		if added {
			// 如设置了则更新内存缓存中的值
			err = c.local.Set(key, value, opts...)
			return err == nil, err
		}
		return
	}
	return c.local.SetWithNotExists(key, value, opts...)
}

// Incr 原子性地将键对应的值增加1, 并返回新的值
//   - 如果键不存在, 则将原子值设为1 (Set Use Opts)
//   - 如果键存在, 则更新的新值(Set Without Opts)
func (c *L2Cache) Incr(key string, opts ...storage.EntryOptions) (int64, error) {
	return c.IncrBy(key, 1, opts...)
}

// IncrBy 原子性地将键对应的值增加Step步进值, 并返回新的值
//   - 如果键不存在, 则将原子值设为Step (Set Use Opts)
//   - 如果键存在, 则更新的新值(Set Without Opts)
func (c *L2Cache) IncrBy(key string, step int64, opts ...storage.EntryOptions) (int64, error) {
	if key == "" {
		return 0, storage.ErrKeyIsEmpty
	}
	sLock := c.getSelfLock(key)
	sLock.Lock()
	defer sLock.Unlock()

	// 这里使用 remote 处理, 并即时手动更新内存缓存中的值
	val, err := c.remote.IncrBy(key, step, opts...)
	if err == nil {
		// 更新内存缓存中的值
		err = c.local.Set(key, val, opts...)
	}
	return val, err
}

// Decr 原子性地将键对应的值减少1, 并返回新的值
//   - 如果键不存在, 则将原子值设为 -1 (Set Use Opts)
//   - 如果键存在, 则更新的新值(Set Without Opts)
func (c *L2Cache) Decr(key string, opts ...storage.EntryOptions) (int64, error) {
	return c.DecrBy(key, 1, opts...)
}

// DecrBy 原子性地将键对应的值减少Step步进值, 并返回新的值
//   - 如果键不存在, 则将原子值设为 -step (Set Use Opts)
//   - 如果键存在, 则更新的新值(Set Without Opts)
func (c *L2Cache) DecrBy(key string, step int64, opts ...storage.EntryOptions) (int64, error) {
	if key == "" {
		return 0, storage.ErrKeyIsEmpty
	}
	sLock := c.getSelfLock(key)
	sLock.Lock()
	defer sLock.Unlock()

	val, err := c.remote.DecrBy(key, step, opts...)
	if err == nil {
		// 更新内存缓存中的值
		err = c.local.Set(key, val, opts...)
	}
	return val, err
}

// LoadDo 获取键对应的值, 并反序列化解析后赋值到 refVal
//   - refVal 是一个指向具体类型的指针, 用于存放反序列化后的数据
//   - 缓存存在: 则反序列化解析后赋值到 refVal
//   - 缓存不存在: 则执行 Fn 函数获取查询值后设置为缓存(Set Use Opts), 最后赋值到 refVal
//   - 注意: 请确保 refVal(&T) 的类型和 do() 函数的返回值T类型一致
func (c *L2Cache) LoadDo(key string, refVal any, do func() (setVal any, err error), opts ...storage.EntryOptions) error {
	if key == "" {
		return storage.ErrKeyIsEmpty
	}
	if refVal == nil {
		return storage.ErrRefValueIsNil
	}

	sLock := c.getSelfLock(key)
	sLock.Lock()
	defer sLock.Unlock()

	// 先在 local 缓存中查找
	if c.local.Scan(key, refVal) == nil {
		return nil
	}
	// 若内存缓存中未找到，则执行 remote 加载数据
	err := c.remote.LoadDo(key, refVal, do, opts...)
	if err == nil {
		c.local.Set(key, refVal, opts...)
	}
	return err
}

// UpdateFn 更新键对应的值
//   - 如果键不存在, 则执行 Fn 函数设置值并返回设置的默认值(Set Use Opts), 并返回设置的默认值
//   - 如果键存在, 则执行 Fn 函数更新值并返回更新的新值(Set Without Opts), 并返回更新的新值(过期时间/Tags保存不变)
func (c *L2Cache) UpdateFn(key string, fn func(getVal any, exits bool) (updateVal any, err error), opts ...storage.EntryOptions) (any, error) {
	if key == "" {
		return nil, storage.ErrKeyIsEmpty
	}

	sLock := c.getSelfLock(key)
	sLock.Lock()
	defer sLock.Unlock()

	val, err := c.remote.UpdateFn(key, fn, opts...)
	if err == nil {
		err = c.local.Set(key, val, opts...)
	}
	return val, err
}

// Expire 设置指定缓存 Key 的存活时间(精度为秒)
func (c *L2Cache) Expire(key string, duration time.Duration) error {
	if key == "" {
		return storage.ErrKeyIsEmpty
	}
	// 先在内存缓存中设置过期时间
	_ = c.local.Expire(key, duration)
	// 若内存缓存中未找到，则在 remote 缓存中设置过期时间
	return c.remote.Expire(key, duration)
}

// ExpireAt 设置指定缓存 Key 的存活时间(精度为秒)
func (c *L2Cache) ExpireAt(key string, expireAt time.Time) error {
	if key == "" {
		return storage.ErrKeyIsEmpty
	}
	// 先在内存缓存中设置过期时间
	_ = c.local.ExpireAt(key, expireAt)
	// 若内存缓存中未找到，则在Redis缓存中设置过期时间
	return c.remote.ExpireAt(key, expireAt)
}

// TTL 获取键的剩余存活时间(精度为秒)
//   - 不存在或如果已经过期则返回-1
//   - 如果设置了永久存活则返回0
func (c *L2Cache) TTL(key string) time.Duration {
	if key == "" {
		return -1
	}
	// 先在内存缓存中查找
	ttl := c.local.TTL(key)
	if ttl != -1 {
		return ttl
	}
	// 若内存缓存中未找到，则在Redis缓存中查找
	ttl = c.remote.TTL(key)
	return ttl
}

// Contains 判断缓存 Key 是否存在, 支持多个键查询, 只要有一个键不存在则返回false
func (c *L2Cache) Contains(keys ...string) bool {
	if len(keys) == 0 {
		return false
	}
	// 先在内存缓存中查找
	if c.local.Contains(keys...) {
		return true
	}
	// 若内存缓存中未找到，则在Redis缓存中查找
	if c.remote.Contains(keys...) {
		return true
	}
	return false
}

// ContainsSome 判断缓存 Key 是否存在, 支持多个键查询, 如果有一个或多个键存在则返回true
func (c *L2Cache) ContainsSome(keys ...string) bool {
	if len(keys) == 0 {
		return false
	}
	// 先在内存缓存中查找
	if c.local.ContainsSome(keys...) {
		return true
	}
	// 若内存缓存中未找到，则在 remote 缓存中查找
	if c.remote.ContainsSome(keys...) {
		return true
	}
	return false
}

// ContainsTags 检查 Tag 标签是否存在
func (c *L2Cache) ContainsTags(tags ...string) bool {
	if len(tags) == 0 {
		return false
	}
	// 先在内存缓存中查找
	if c.local.ContainsTags(tags...) {
		return true
	}
	// 若内存缓存中未找到，则在 remote 缓存中查找
	if c.remote.ContainsTags(tags...) {
		return true
	}
	return false
}

// ContainsSomeTags 检查 Tag 标签是否存在, 支持多个键查询, 如果有一个或多个键存在则返回true
func (c *L2Cache) ContainsSomeTags(tags ...string) bool {
	if len(tags) == 0 {
		return false
	}
	// 先在内存缓存中查找
	if c.local.ContainsSomeTags(tags...) {
		return true
	}
	// 若内存缓存中未找到，则在 remote 缓存中查找
	if c.remote.ContainsSomeTags(tags...) {
		return true
	}
	return false

}

// ContainsTagKey 检查 Tag/Key 标签对是否存在
func (c *L2Cache) ContainsTagKey(tag, key string) bool {
	if tag == "" || key == "" {
		return false
	}
	// 先在内存缓存中查找
	if c.local.ContainsTagKey(tag, key) {
		return true
	}
	// 若内存缓存中未找到，则在 remote 缓存中查找
	return c.remote.ContainsTagKey(tag, key)
}

// Metrics 返回监控指标
//   - 注意: 仅返回内存缓存部分的监控指标, Redis 部分不会计算在内
func (c *L2Cache) Metrics() *storage.Metrics {
	return c.local.Metrics()
}

// Cleanup 手动清理过期数据
func (c *L2Cache) Cleanup() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	// 仅清理内存缓存, Redis 自动维护过期数据, 无需手动清理
	return c.local.Cleanup()
}

// Delete 依据缓存key删除缓存项
func (c *L2Cache) Delete(keys ...string) error {
	if len(keys) == 0 {
		return nil
	}
	c.local.Delete(keys...)
	return c.remote.Delete(keys...)
}

// DeleteTags 依据缓存Tag标签删除缓存项, 同时tag 标签以及对应的 key <=> tags 和 tag <=> keys 的索引集合数据
func (c *L2Cache) DeleteTags(tags ...string) error {
	if len(tags) == 0 {
		return nil
	}
	c.local.DeleteTags(tags...)
	return c.remote.DeleteTags(tags...)
}

// Clear 清空所有数据(同步操作)
//   - 注意: 仅清空内存缓存部分, Redis 部分不会清除
func (c *L2Cache) Clear() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.local != nil {
		c.local.Clear()
	}
	return nil
}

// ClearAsync 清空所有数据(异步操作)
//   - 注意: 仅清空内存缓存部分, Redis 部分不会清除
func (c *L2Cache) ClearAsync() error {
	c.mu.Lock()
	defer c.mu.Unlock()
	if c.local != nil {
		go c.local.Clear()
	}
	return nil
}

// TotalSize 返回当前缓存的总大小(单位: byte)
//   - 注意: 仅返回内存缓存部分的总大小, Redis 部分不会计算在内
func (c *L2Cache) TotalSize() int64 {
	return c.local.TotalSize()
}

// TotalCount 返回当前缓存项(Key)的总数量(单位: 个)
//   - 注意: 仅返回内存缓存部分的项数量, Redis 部分不会计算在内
func (c *L2Cache) TotalCount() int64 {
	return c.local.TotalCount()
}

// MaxSize 返回当前缓存实例的最大容量(单位: byte)
//   - 注意: 仅返回内存缓存部分的容量, Redis 部分不会计算在内
func (c *L2Cache) MaxSize() int64 {
	return c.local.MaxSize()
}

// MaxEntrySize 获取单个缓存项数据限制的大小(单位: byte)
//   - 注意: 仅返回内存缓存部分的容量, Redis 部分不会计算在内
func (c *L2Cache) MaxEntrySize() int64 {
	return c.local.MaxEntrySize()
}

// StorageType 获取缓存存储驱动类型
func (c *L2Cache) StorageType() storage.StorageType {
	return storage.StorageL2Cache
}

// DefaultExpire 获取默认过期时间
//   - 注意: 仅返回内存缓存部分的默认过期时间, Redis 部分不会计算在内
func (c *L2Cache) DefaultExpire() time.Duration {
	return c.local.DefaultExpire()
}

// UseLocal 仅使用 Local 缓存进行操作( 通常为 MemoryCache )
//   - 仅在 L2Cache 驱动模式下有效, 其他驱动模式返回自身驱动实例
func (c *L2Cache) UseLocal() storage.Storager {
	return c.local
}

// UseRemote 仅仅使用 Remote 进行缓存操作( 通常为 RedisCache )
//   - 仅在 L2Cache 驱动模式下有效, 其他驱动模式返回自身驱动实例
func (c *L2Cache) UseRemote() storage.Storager {
	return c.remote
}

// LiveState 获取当前缓存实例的运行状态
func (c *L2Cache) LiveState() storage.LiveState {
	// 若其中一个缓存实例为nil, 则整个 L2Cache 视为异常状态
	if c.local == nil || c.remote == nil {
		return storage.LiveStateFailure
	}
	// 若两个缓存实例都处于正常状态, 则整个 L2Cache 视为正常状态
	if c.local.LiveState() == storage.LiveStateNormal &&
		c.remote.LiveState() == storage.LiveStateNormal {
		return storage.LiveStateNormal
	}
	// 若两个缓存实例都处于关闭状态, 则整个 L2Cache 视为关闭状态
	if c.local.LiveState() == storage.LiveStateShutdown &&
		c.remote.LiveState() == storage.LiveStateShutdown {
		return storage.LiveStateShutdown
	}
	// 其他情况视为异常状态
	return storage.LiveStateFailure
}

// IsGlobal 是否为全局缓存实例
func (c *L2Cache) IsGlobal() bool {
	return c.isGlobal
}

// setGlobal 设置实例为全局缓存标记
func (c *L2Cache) SetGlobal() {
	c.isGlobal = true
	c.local.SetGlobal()
	c.remote.SetGlobal()
}
